package afcp.alumni.dao.base;

import java.util.List;
import java.util.Locale;

import afcp.alumni.model.SearchResult;
import afcp.alumni.model.base.DatastoreEntity;
import afcp.alumni.model.base.UniqueField;
import afcp.alumni.model.base.WithUniqueField;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

public abstract class DaoDatastoreCommon<T extends DatastoreEntity> {

	protected abstract Class<T> getClassObject();

	protected Class<T> classObject = this.getClassObject();
	protected String className = classObject.getSimpleName();
	
	protected DaoDatastoreCommon() {
	}
	
	protected abstract DatastoreEntity getEntityAncestor();
	
	protected Key getAncestorKey() {
		DatastoreEntity entityAncestor = getEntityAncestor();
		return entityAncestor != null ? entityAncestor.getKey() : null;
	}
	
	protected void setKey(T entity) {
		if (entity.getKey() == null) {
			entity.setKeyWithAncestor(getAncestorKey());
		}
	}
	protected void setKey(List<T> entities) {
		for (T entity : entities) {
			if (entity.getKey() == null) {
				entity.setKeyWithAncestor(getAncestorKey());
			}
		}
	}

	/**
	 * Returns a list of all the entities of this kind (<em>T</em>) in the
	 * datastore.
	 * 
	 * @return All the entities of kind <em>T</em> in datastore.
	 */
	public abstract List<T> findAll();
	
	public abstract SearchResult<T> findOnePage(int page, int perPage, Locale locale);

	/**
	 * <p>
	 * Find and return the entity with the given id (the string form).
	 * </p>
	 * <p>
	 * Warning: The returned entity is detached. For instance, it can't be used
	 * to remove it from the datastore.
	 * </p>
	 * 
	 * @param id
	 * @return
	 */
	public abstract T find(String id);
	
	public abstract T findFromKeyString(String key);

	/**
	 * <p>
	 * Find and return the entity with the unique field.
	 * </p>
	 * <p>
	 * Warning: The returned entity is detached. For instance, it can't be used
	 * to remove it from the datastore.
	 * </p>
	 * 
	 * @param id
	 * @return
	 */
	public abstract <U extends WithUniqueField> WithUniqueField find(UniqueField<U> field);

	public abstract SearchResult<T> findFromKeyStrings(List<String> ids, int page, int perPage, Locale locale);

	public abstract boolean exists(T entity);

	/**
	 * Adds the given entity to the datastore. Its Id must be set and not be
	 * already used in the datastore. If the id is already used, an error log is
	 * writen.
	 * 
	 * @param entity
	 *            The entity to add to the datastore.
	 * @return True if the entity was successfully added to the datastore, false
	 *         otherwise.
	 */
	public abstract boolean addOrUpdate(T entity);

	public abstract boolean addOrUpdate(List<T> entities);

	/**
	 * Removes the entity with the given id in the string form.
	 * 
	 * @param id
	 *            The id of the entity to delete. The method
	 *            {@link KeyFactory#stringToKey(String)} is used to find the key
	 *            from this String. It means that this id must have been
	 *            generated using the method {@link KeyFactory#keyToString(Key)}.
	 * @return
	 */
	public abstract T removeFromKeyString(String id);

	/**
	 * Removes the entity with the given id which is the {@link String} form of the <code>long</code> key id.
	 * 
	 * @param id
	 *            The {@link String} form of the id. Use the {@link KeyFactory} and
	 *            the {@link Key} objects to convert the key id from long to
	 *            {@link String}.
	 * @return
	 */
	public abstract T remove(String id);

	public abstract boolean remove(List<String> entities);
	
	public abstract boolean removeAll();

	/**
	 * Updates an entity still attached to the datastore.
	 * 
	 * @param entity
	 *            The entity to update in the datastore
	 * @return 
	 */
	public abstract boolean update(T entity);
	
//	public abstract long getTotalResults();

}
